﻿using System;
using System.IO;
using System.Linq;
using System.Threading;
using FFmpegVideoClip.Lib.AdvancedFragmentProcessor;
using FFmpegVideoClip.Lib.AdvancedFragmentProcessor.InternalProcessor;
using FFmpegVideoClip.Mode.Files;
using UtilityToolsCollect.ObjectsLibrary.ProcessManage;

namespace FFmpegVideoClip.Lib.Afterprocessings
{
    //后处理，因为需要采集生成后的文件信息，所以必须在标准处理及上一流程，执行并生成文件后才能实例化
    public class AfterprocessingTaskFlow : TaskFlowAbstract
    {
        public readonly record struct InputInfo(TimeSpan Duration, string InFilePath);
        public InputInfo MaxDurationInputInfo { get; private set; }
        public InputInfo[] InputsInfo { get; private set; } = null!;

        private int FLowIndex;
        private AdvancedParametersSchemeModule[] modules;
        private CancellationTokenSource cancellationTokenSource = new();
        private ProcessShell? m_processShell;

        public bool IsCancellationRequested => Tools.IsCancellationRequested || cancellationTokenSource.IsCancellationRequested;

        public AfterprocessingTaskFlow(Clip clip, AdvancedParametersSchemeModule[] modules, int FragmentID, int FLowIndex)
        {
            ThisClip = clip;
            this.modules = modules;
            this.FLowIndex = FLowIndex;
            module = modules[FLowIndex];

            ThisFragment = new Fragment
            {
                clip = ThisClip,
                Name = ThisClip.Name,
                Index = 0,
                ID = FragmentID,
                StartTimeValue = Tools.TimeZero,

                EndTimeValue = Tools.GetTimeSpanToString(clip.TimeDuration),
                InputPath = string.Empty,
                TempWritePath = string.Empty
            };

            successAndProgress = new SuccessAndProgress(module, ThisFragment, true);
        }
        public void Init(string StandardFlowsFinalResultFilePath)
        {
            if (IsCancellationRequested) return;
            //处理输入-输入项的参数 -1 是原始的音视频源，-2是标准处理的结果源，从0开始及之后的索引是流程的输出结果作为输入的源
            if (!module.GetInputs(out ParametersSign[] Inputs))
                throw new ArgumentException($"剪辑[{ThisClip!.Name}]->后处理流程[{module.SchemeName}] 缺少输入参数");

            InputsInfo = new InputInfo[Inputs.Length];
            int SelectIndex;

            for (int i = 0; i < Inputs.Length; i++)
            {
                ParametersSign CurrentInputParame = Inputs[i];
                if (!Tools.InputToIndex(CurrentInputParame.Parameter, out SelectIndex) || SelectIndex < -2)
                    throw new ArgumentException($"剪辑[{ThisClip!.Name}]->后处理流程[{module.SchemeName}]->[{CurrentInputParame.SignName}]:输入参数异常，不是一个可以转换的值，或者值小于【-2】");
                //选择的输入源的输入
                switch (SelectIndex)
                {
                    case -1:
                        CurrentInputParame.Parameter = ThisClip!.Fragments.First().InputPath;
                        if (ThisClip!.Fragments.Count > 1 && ThisClip!.Fragments.Any(p => !p.InputPath.Equals(CurrentInputParame.Parameter, StringComparison.OrdinalIgnoreCase)))
                            throw new InvalidOperationException($"剪辑[{ThisClip!.Name}]->后处理流程[{module.SchemeName}]->[{CurrentInputParame.SignName}]:输入设置错误，不支持该操作，" +
                                $"因为剪辑中的片段来源于多个文件，因此无法指定原始文件作为后处理的输入源");
                        break;
                    case -2:
                        CurrentInputParame.Parameter = StandardFlowsFinalResultFilePath;
                        break;
                    default:
                        if (SelectIndex >= FLowIndex)
                            throw new ArgumentException($"剪辑[{ThisClip!.Name}]->后处理流程[{module.SchemeName}]->[{CurrentInputParame.SignName}]" +
                                "输入流选择错误,在流程中目标流位于当前流之后，目标流的输出结果尚未产生，无法作为当前流的输入");
                        CurrentInputParame.Parameter = modules[SelectIndex].GetOutput().Parameter;
                        break;
                }
                if (!File.Exists(CurrentInputParame.Parameter))
                    throw new ArgumentException($"剪辑[{ThisClip!.Name}]->后处理流程[{module.SchemeName}]->[{CurrentInputParame.SignName}]:输入文件[{CurrentInputParame.Parameter}]不存在");
                InputsInfo[i] = new InputInfo(GetMediaInfo.GetInfo(CurrentInputParame.Parameter).Duration, CurrentInputParame.Parameter);
            }
            MaxDurationInputInfo = InputsInfo.MaxBy(p => p.Duration);

            //处理输出
            string Extension = module.GetOutput().Parameter.Trim();
            if (!Extension.StartsWith('.'))
                Extension = '.' + Extension;

            string path = ThisClip!.TempCacheDirectory;
            if (!Directory.Exists(path))
                Directory.CreateDirectory(path);
            if (modules.Last() == module)
            {
                path = Path.Combine(ThisClip.OutDirectory, $"{ThisClip.Name}{Extension}");
                while (File.Exists(path))
                    path = Path.Combine(ThisClip.OutDirectory, $"{Path.GetFileNameWithoutExtension(path)}(重命名){Extension}");
            }
            else
            {
                path = Path.Combine(path, $"{ThisClip.Name}_AfterprocessingFlow_{FLowIndex}{Extension}");
                if (File.Exists(path))
                    File.Delete(path);
            }
            module.GetOutput().Parameter = path;

            ThisFragment.EndTimeValue = Tools.GetTimeSpanToString(MaxDurationInputInfo.Duration);
            ThisFragment.InputPath = MaxDurationInputInfo.InFilePath;
            ThisFragment.TempWritePath = module.GetOutput().Parameter;

            //处理时间
            if (module.GetParametersSign(ParametersSign.ETime, out ParametersSign? ETime))
                ETime!.Parameter = Tools.GetTimeSpanToString(MaxDurationInputInfo.Duration);

            if (module.GetParametersSign(ParametersSign.STime, out ParametersSign? STime))
                STime!.Parameter = Tools.TimeZero;


        }

        public override void Execute()
        {
            if (IsCancellationRequested) return;
            string InFileFullPath = module.GetInput().Parameter;
            string OutFileFullPath = module.GetOutput().Parameter;
            successAndProgress.Init();
            successAndProgress.WriteLineNewInfo($"剪辑[{ThisClip?.Name}]->流程[{module.SchemeName}]");
            if (string.IsNullOrEmpty(InFileFullPath) || !File.Exists(InFileFullPath))
            {
                successAndProgress.ErrorInfo = $"[{InFileFullPath}]输入文件不存在";
                successAndProgress.CompletedTimingSeconds = successAndProgress.TotalSeconds;
                LogManage.WriteLineNewInfo(successAndProgress.ErrorInfo, true);
                return;
            }
            if (string.IsNullOrEmpty(OutFileFullPath) || !Directory.Exists(Path.GetDirectoryName(OutFileFullPath)))
            {
                successAndProgress.ErrorInfo = $"[{OutFileFullPath}]输出路径不存在";
                successAndProgress.CompletedTimingSeconds = successAndProgress.TotalSeconds;
                LogManage.WriteLineNewInfo(successAndProgress.ErrorInfo, true);
                return;
            }

            if (File.Exists(OutFileFullPath))
                File.Delete(OutFileFullPath);

            string args = module.GenerateCommandArgument();
            successAndProgress.WriteLineNewInfo($"{module.ProcessorName} {args}");
            if (IsCancellationRequested) return;
            CallShell.ShellTypeProcessNameIFSet(module);


            successAndProgress.EnableWriteLog = module.EnableSkipOutputSign;


            if (InternalProcessorSelector.GetSelector(module, out Action<AdvancedParametersSchemeModule, SuccessAndProgress> ProcessExecute))
                ProcessExecute?.Invoke(module, successAndProgress);
            else
                ProcessShellProcessExecute(module.ProcessorName, args, successAndProgress.SuccessValidated, successAndProgress.SuccessValidated);
            if (IsCancellationRequested) return;
            if (!successAndProgress.IsSuccess)
                successAndProgress.ErrorInfo = $"处理错误:参数:[{module.ProcessorName}] [{args}]";

            successAndProgress.CompletedTimingSeconds = successAndProgress.TotalSeconds;


            if (successAndProgress.EnableWriteLog)
                File.WriteAllText(OutFileFullPath, successAndProgress.Log);


            LogManage.WriteLineNewInfo(successAndProgress.Log);
            successAndProgress.ReleaseScript();

            if (!successAndProgress.IsSuccess)
                throw new AfterprocessingTaskFlowException($"{successAndProgress.ErrorInfo} Log [{successAndProgress.Log}]");
        }
        private void ProcessShellProcessExecute(string ProcessName, string Args, Action<string?>? OutputReceived, Action<string?>? ErrorReceived)
        {
            m_processShell = new ProcessShell();
            m_processShell.ExecuteWaitForExit(ProcessName, Args, OutputReceived, ErrorReceived);
        }
        public void Cancel()
        {
            cancellationTokenSource.Cancel();
            OnDispose();
        }

        protected override void OnDispose()
        {
            InputsInfo = null!;
            modules = null!;
            cancellationTokenSource.Dispose();
            cancellationTokenSource = null!;
            m_processShell?.Dispose();
            m_processShell = null;
            base.OnDispose();
        }
    }

    [Serializable]
    public class AfterprocessingTaskFlowException : Exception
    {
        public AfterprocessingTaskFlowException() { }
        public AfterprocessingTaskFlowException(string message) : base(message) { }
        public AfterprocessingTaskFlowException(string message, Exception inner) : base(message, inner) { }
        [Obsolete]protected AfterprocessingTaskFlowException(
          System.Runtime.Serialization.SerializationInfo info,
          System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
    }

}
